﻿using ReaderWriter.IO;
using ReaderWriter.Providers.Data;
using System;
using System.Collections.Generic;
using System.Data.Entity.Validation;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ReaderWriter.Providers
{
    public static class IntLongExtn
    {
        public static long? ToLong(this string longValue)
        {
            long v = long.MinValue;
            long.TryParse(longValue, out v);
            return (v == long.MinValue || v == 0) ? null : new Nullable<long>(v);
        }
    }
    public class WebSqlReaderWriter : IReaderWriter
    {
        string _baseUri;
        PortalEntities db;
        public static int SEGMENT_SIZE = 512 * 1024;
        public long? UriToFolderID(string uri)
        {
            //http://localhost:7452/13131
            long? pid = uri.Replace(_baseUri, string.Empty).ToLong();
            return pid;
        }
        public long? UriToFileID(string uri)
        {
            //http://localhost:7452/24167/214213
            string folderFiles = uri.Replace(_baseUri, string.Empty);
            long? pid = (folderFiles.Split("\\/".ToCharArray())[1]).ToLong();
            return pid;
        }
        public WebSqlReaderWriter(string baseUri)
        {
            _baseUri = baseUri;
            db = new PortalEntities();
        }

        ~WebSqlReaderWriter()
        {
            db.Dispose();
        }
        private FolderNode ReadFolderFromDirectory(WebFolder dr)
        {
            if (dr == null) return null;
            FolderNode folder = new FolderNode(this);
            folder.Created = dr.Created.HasValue ? dr.Created.Value : DateTime.Now;
            folder.Name = dr.Name;
            folder.Parent = dr.ParentId == null ? null : ReadFolderFromDirectory(dr.WebFolder2);
            folder.Updated = dr.Updated.HasValue ? dr.Updated.Value : DateTime.Now;
            folder.Uri = _baseUri + dr.FolderId;
            folder.User = Environment.UserName;
            folder.StorageType = StorageType.Folder;
            folder.InternalID = dr.FolderId;
            return folder;
        }
        private FileNode ReadFileFromDirectory(WebFile info, FolderNode parent=null)
        {
            if (info == null) return null;
            FileNode file = new FileNode(this);
            file.Created = info.Created.HasValue ? info.Created.Value : DateTime.Now;
            file.Name = info.Name;
            file.Parent = parent;// info.Directory == null ? null : ReadFolderFromDirectory(info.Directory);
            file.Updated = info.Updated.HasValue ? info.Updated.Value : DateTime.Now;
            file.Uri = _baseUri + info.FolderId + "/" + info.FileId;
            file.User = Environment.UserName;
            file.StorageType = StorageType.File;
            file.InternalID = info.FileId;
            file.Size = 200;
            return file;
        }
        public FolderNode GetRoot()
        {
            var f = db.WebFolders.Where(x => x.ParentId == null).FirstOrDefault();
            return ReadFolderFromDirectory(f);
        }

        public FolderNode Up(string uri)
        {
            long? pid = UriToFolderID(uri);
            WebFolder dir = db.WebFolders.Where(x => x.FolderId == pid).FirstOrDefault();
            if (dir != null && dir.WebFolder2!=null)
            {
                return ReadFolderFromDirectory(dir.WebFolder2);
            }
            return null;
        }

        public List<FolderNode> GetFolders(FolderNode root)
        {
            List<FolderNode> folders = new List<FolderNode>();
            var dirs = db.WebFolders.Where(x => x.ParentId == root.InternalID);
            foreach (var d in dirs)
            {
                folders.Add(ReadFolderFromDirectory(d));
            }
            return folders;
        }

        public List<FolderNode> GetFolders(string uri, bool recurse, int level=0)
        {
            List<FolderNode> folders = new List<FolderNode>();
            long? pid = UriToFolderID(uri);
            IQueryable<WebFolder> dirs = (pid.HasValue)
                ? db.WebFolders.Where(x => x.ParentId == pid)
                : db.WebFolders.Where(x => x.ParentId.HasValue == false);
            foreach (var d in dirs)
            {
                folders.Add(ReadFolderFromDirectory(d));
            }
            return folders;
        }
        public List<StorageNode> GetAllNodes(string uri)
        {
            List<StorageNode> folders = new List<StorageNode>();
            long? pid = UriToFolderID(uri);
            IQueryable<WebFolder> dirs = (pid.HasValue)
                ? db.WebFolders.Where(x => x.ParentId == pid)
                : db.WebFolders.Where(x => x.ParentId.HasValue == false);
            IQueryable<WebFile> files = (pid.HasValue)
                ? db.WebFiles.Where(x => x.FolderId == pid)
                : db.WebFiles.Where(x => x.FolderId.HasValue == false);
            foreach (var d in dirs)
            {
                folders.Add(ReadFolderFromDirectory(d));
            }
            var root = ReadFolderFromDirectory(db.WebFolders.Where(x => x.FolderId == pid).FirstOrDefault());
            foreach (var f in files)
            {
                folders.Add(ReadFileFromDirectory(f, root));
            }
            return folders;
        }

        public List<FileNode> GetFiles(FolderNode parent)
        {
            throw new NotImplementedException();
        }

        public FolderNode GetFolder(string uri)
        {
            long? pid = UriToFolderID(uri);
            IQueryable<WebFolder> dirs = db.WebFolders.Where(x => x.FolderId == pid);
            return ReadFolderFromDirectory(dirs.FirstOrDefault());
        }

        public FileNode GetFile(string uri, bool fillWithContent = false)
        {
            throw new NotImplementedException();
        }

        public FileNode CreateFile(FolderNode parent, FileNode file, bool overriteProperties = true)
        {
            throw new NotImplementedException();
        }

        public FolderNode CreateFolder(FolderNode parent, FolderNode child, bool overriteProperties = true)
        {
            WebFolder folder = new WebFolder();
            folder.Name = child.Name;
            folder.ParentId = parent.InternalID;
            folder.Created = DateTime.Now;
            folder.Updated = DateTime.Now;
            folder.AppUser = Environment.UserName;
            db.WebFolders.Add(folder);
            db.SaveChanges();
            child.InternalID = folder.FolderId;
            child.Parent = parent;
            return child;
        }

        public FileNode WriteFile(FileNode file)
        {
            int fileSize = file.Data.Length;
            FolderNode parent = file.Parent;
            //long id  = DateTime.Now.Ticks;
            WebFile wFile = new WebFile();
            //wFile.FileId = id;
            wFile.FolderId = parent.InternalID;
            wFile.Name = file.Name;
            wFile.Created = DateTime.Now;
            wFile.Updated = DateTime.Now;
            wFile.AppUser = Environment.UserName;
            wFile.Size = fileSize;
            db.WebFiles.Add(wFile);
            try
            {
                db.SaveChanges();
            }
            catch (DbEntityValidationException e)
            {
                foreach (var eve in e.EntityValidationErrors)
                {
                    Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                        eve.Entry.Entity.GetType().Name, eve.Entry.State);
                    foreach (var ve in eve.ValidationErrors)
                    {
                        Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
                            ve.PropertyName, ve.ErrorMessage);
                    }
                }
                throw;
            }
            
            long id = wFile.FileId;
            if (fileSize > SEGMENT_SIZE)
            {
                int totalSegments = fileSize / SEGMENT_SIZE;
                totalSegments++;

                for (int i = 0; i < totalSegments; i++)
                {
                    Segment segment = new Segment();
                    segment.SegmentId = DateTime.Now.Ticks;
                    segment.FileId = id;
                    segment.OrderId = i;
                    int index = i * SEGMENT_SIZE;
                    int remaining = ((index + SEGMENT_SIZE) > fileSize) ? (fileSize - index) : SEGMENT_SIZE;
                    byte[] data = file.Data.Skip(index).Take(remaining).ToArray();
                    segment.Bytes = data;                    
                    segment.Created = DateTime.Now;
                    segment.Updated = DateTime.Now;
                    segment.AppUser = Environment.UserName;
                    db.Segments.Add(segment);
                }
            }
            else
            {
                Segment segment = new Segment();
                segment.FileId = id;
                segment.OrderId = 0;
                segment.Bytes = file.Data;
                segment.Created = DateTime.Now;
                segment.Updated = DateTime.Now;
                segment.AppUser = Environment.UserName;
                db.Segments.Add(segment);
            }
            db.SaveChanges();

            return file;
        }

        public FileNode ReadFile(FileNode file)
        {
            long? pid = UriToFileID(file.Uri);
            var wFile = db.WebFiles.Where(x => x.FileId == pid).FirstOrDefault();
            file = ReadFileFromDirectory(wFile);
            file.Data = ReadFileData(wFile);
            return file;
        }

        public bool IsFolderExist(FolderNode folder)
        {
            throw new NotImplementedException();
        }

        public bool IsFileExist(FileNode file)
        {
            throw new NotImplementedException();
        }

        public bool DeleteFile(FileNode file)
        {
            long? pid = UriToFileID(file.Uri);
            var wFile = db.WebFiles.Where(x => x.FileId == pid).FirstOrDefault();
            db.Database.ExecuteSqlCommand("delete from Segments where FileId = {0}", wFile.FileId);
            db.Database.ExecuteSqlCommand("delete from WebFile where FileId = {0}", wFile.FileId);
            return true;
        }

        public bool RemoveFolder(FolderNode folder)
        {
            throw new NotImplementedException();
        }
        //public void OpenFile(string uri)
        //{
        //    string fileName = GetARootedFileName(uri);          
        //    if (Path.IsPathRooted(fileName))
        //    {
        //        Task task = new Task(() =>
        //        {
        //            ProcessStartInfo os = new ProcessStartInfo(fileName);
        //            os.UseShellExecute = true;
        //            os.Verb = "Open";
        //            Process p = Process.Start(os);
        //        });
        //        task.RunSynchronously();
        //    }
        //}      

        public string Execute(string uri)
        {
            string fileName = GetRootedFileName(uri);        
            if (Path.IsPathRooted(fileName))
            {
                ProcessStartInfo os = new ProcessStartInfo(fileName);
                os.UseShellExecute = true;
                os.Verb = "Open";
                Process p = Process.Start(os);
            }
            return fileName;
        }
        public string GetRootedFileName(string uri)
        { 
            var file = GetFile(uri);
            string fileName = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache) + "\\" + Guid.NewGuid().ToString() + file.Name;
            byte[] fileData = new byte[0]; 
            fileData = ReadFileData(file);
            System.IO.File.WriteAllBytes(fileName, fileData);
            return fileName;
        }        
        public WebFile GetFile(string uri)
        { 
            long? pid = UriToFileID(uri);
            var file = db.WebFiles.Where(x => x.FileId == pid).FirstOrDefault();
            return file;
        }
        private byte[] ReadFileData(WebFile file)
        {
            byte[] fileData = new byte[0]; ;
            int totalSegments = file.Segments.Count();
            Segment[] segments = file.Segments.OrderBy(x => x.OrderId).ToArray();
            if (segments.Count() == 1)
            {
                fileData = segments.FirstOrDefault().Bytes;
            }
            else
            {
                int fileSize = (int)file.Size;
                for (int i = 0; i < totalSegments; i++)
                {
                    Segment segment = segments[i];
                    fileData = fileData.Concat(segment.Bytes).ToArray();
                }
            }
            return fileData;
        }
    }
}
